using System.Collections;
using System.Xml;
using MessageGateway;

namespace Aggregator
{
    public delegate void OnAggregatorMessageReceivedEvent(XmlDocument msg, string corrID);
    public delegate void OnAggregatorMessageSentEvent(XmlDocument msg, string corrID);

    public class AggregatorComponent : SimpleFilter
    {
        protected string correlationPath;
        protected IAggregateFactory aggregateFactory;

        protected OnAggregatorMessageReceivedEvent receivedEvent;
        protected OnAggregatorMessageSentEvent sentEvent;

        protected IDictionary activeAggregates = new Hashtable();

        public AggregatorComponent(IMessageReceiver input, IMessageSender output, 
            string correlationPath, IAggregateFactory aggregateFactory)
            : base (input, output)
        {
            this.correlationPath = correlationPath;
            this.aggregateFactory = aggregateFactory;
            receivedEvent = new OnAggregatorMessageReceivedEvent(NullEvent);
            sentEvent = new OnAggregatorMessageSentEvent(NullEvent);
        }

        public AggregatorComponent(string inputChannel, string outputChannel, 
            string correlationPath, IAggregateFactory aggregateFactory, 
            IMessageGatewayFactory factory)       
            : this (factory.GetReceiverInstance(inputChannel), factory.GetSenderInstance(outputChannel), 
            correlationPath, aggregateFactory)
        {
        }

        public void AddEventHandler(OnAggregatorMessageReceivedEvent handler)
        {
            receivedEvent += handler;
        }

        public void AddEventHandler(OnAggregatorMessageSentEvent handler)
        {
            sentEvent += handler;
        }

        protected override void OnMessage(XmlDocument message)
        {
            string corrID = GetCorrelationID(message);
            receivedEvent(message, corrID);
            if (corrID != null) 
            {
                IAggregate aggregate = (IAggregate)activeAggregates[corrID];
                if (aggregate == null) 
                {
                    aggregate = aggregateFactory.NewAggregate();
                    activeAggregates.Add(corrID, aggregate);
                }
                if (!aggregate.IsComplete()) 
                {
                    aggregate.AddMessage(message);
                    if (aggregate.IsComplete()) 
                    {
                        XmlDocument result = aggregate.GetResultMessage();
                        output.Send(result);
                        sentEvent(result, corrID);
                    }
                }
            }
        }

        private void NullEvent(XmlDocument message, string corrID){}

        protected string GetCorrelationID(XmlDocument doc)
        {
            XmlNode node = doc.SelectSingleNode(correlationPath);
            if (node == null)
                return null;
            if (node is XmlAttribute) 
            {
                return node.Value;
            }
            else 
            {
                return node.InnerText;
            }
        }

    }
}
